/*
 *  Writer.cpp
 *  Compiler
 *
 *  Created by Jacopo De Stefani on 3/5/12.
 *  Copyright 2012 Politecnico di Milano. All rights reserved.
 *
 */


#include "Writer.h"

using namespace std;

Writer* Writer::uniqueInstance = NULL;

Writer* Writer::getInstance(){
	if (uniqueInstance == NULL) {
		uniqueInstance = new Writer();
	}
	return uniqueInstance;
}

void Writer::initWriter(string progId){
        string codeFilename(progId + ".code");
        string errLogFilename(progId + ".errlog");
    
	gpCodeFile.open(codeFilename.c_str(), ios::out);
	if (!gpCodeFile) {
		cerr << "Error in creating the output file. Terminating the application";
		//exit(-1);
	}
        errorLogFile.open(codeFilename.c_str(), ios::out);
	if (!errorLogFile) {
		cerr << "Error in creating the errorLog file. Terminating the application";
		//exit(-1);
	}
}

//PUSH OPERATIONS
//Writes code corresponding to the move of a int value from sourceAddr on top of the stack.
void Writer::movIntTop(int sourceAddr){
	if(!errFlag){
            gpCodeFile << "ldo i " << sourceAddr << endl;
        }
}

//Writes code corresponding to the move of a bool value from sourceAddr on top of the stack.
void Writer::movBoolTop(int sourceAddr){
	if(!errFlag){
            gpCodeFile << "ldo b " << sourceAddr << endl;
        }
}

//Writes code corresponding to the push of a constant int value on top of the stack.
void Writer::putIntTop(int intValue){
        if(!errFlag){
            gpCodeFile << "ldc i " << intValue << endl;
            gpCodeFile.flush();        }
}

//Writes code corresponding to the push of a constant bool value on top of the stack.
void Writer::putBoolTop(bool boolValue){
	if(!errFlag){
            gpCodeFile << "ldc b " << boolValue << endl;
        }    
}

//Writes code corresponding to the duplication of the int value on top of the stack.
void Writer::dupIntTop(){
        if(!errFlag){
            gpCodeFile << "dup i" << endl;
        }
}
//Writes code corresponding to the duplication of the bool value on top of the stack.
void Writer::dupBoolTop(){
	if(!errFlag){
            gpCodeFile << "dup b" << endl;
        }
}

//POP OPERATIONS
//Writes code corresponding to the store of the int value on top of the stack at destAddr location.
void Writer::storeIntAt(int destAddr){
        if(!errFlag){	
            gpCodeFile << "sro i " << destAddr << endl;
        }
}

//Writes code corresponding to the store of the bool value on top of the stack at destAddr location.
void Writer::storeBoolAt(int destAddr){
        if(!errFlag){
	    gpCodeFile << "sro b " << destAddr << endl;
        }
}

//Writes code corresponding to the pop of the value on top of the stack.
void Writer::popTop(){
        if(!errFlag){
	    gpCodeFile << "pop" << endl;
        }
}

//ARITHMETIC OPERATIONS
//Writes code corresponding to the add of the integer values occuping the two topmost locations on the stack.
void Writer::addTop2(){
        if(!errFlag){
            gpCodeFile << "add i" << endl;
        }
}
//Writes code corresponding to the subtraction of the integer values occuping the two topmost locations on the stack.
void Writer::subTop2(){
        if(!errFlag){
	    gpCodeFile << "sub i" << endl;
        }
}
//Writes code corresponding to the multiplication of the integer values occuping the two topmost locations on the stack.
void Writer::mulTop2(){
        if(!errFlag){
	    gpCodeFile << "mul i" << endl;
        }
}
//Writes code corresponding to the division of the integer values occuping the two topmost locations on the stack.
void Writer::divTop2(){
        if(!errFlag){
	    gpCodeFile << "div i" << endl;
        }
}
//Writes code corresponding to the negation of the value on top of the stack.
void Writer::negTop(){
        if(!errFlag){
	    gpCodeFile << "neg i" << endl;
        }
}
	
//BOOLEAN OPERATIONS
//Writes code corresponding to the logical and of the bool values occuping the two topmost locations on the stack.
void Writer::andTop2(){
        if(!errFlag){
	    gpCodeFile << "and b" << endl;
        }
}
//Writes code corresponding to the logical or of the bool values occuping the two topmost locations on the stack.
void Writer::orTop2(){
        if(!errFlag){
	    gpCodeFile << "or b" << endl;
        }
}
//Writes code corresponding to the logical not of the value on top of the stack.
void Writer::notTop(){
        if(!errFlag){
	    gpCodeFile << "not b" << endl;
        }
}
	
//COMPARISON OPERATIONS
//Writes code corresponding to the "greater or equal than" comparison of the int values occuping the two topmost locations on the stack.
void Writer::geqTop2(){
        if(!errFlag){
	    gpCodeFile << "geq i" << endl;
        }
}
//Writes code corresponding to the "less or equal than" comparison of the int values occuping the two topmost locations on the stack.
void Writer::leqTop2(){
        if(!errFlag){
	    gpCodeFile << "leq i" << endl;
        }
}
//Writes code corresponding to the "greater than" comparison of the int values occuping the two topmost locations on the stack.
void Writer::grtTop2(){
        if(!errFlag){
            gpCodeFile << "grt i" << endl;
        }
}
//Writes code corresponding to the "less than" comparison of the int values occuping the two topmost locations on the stack.
void Writer::lesTop2(){
        if(!errFlag){
            gpCodeFile << "les i" << endl;
        }
}
//Writes code corresponding to the "equal to" comparison of the int values occuping the two topmost locations on the stack.
void Writer::equTop2(){
        if(!errFlag){
	    gpCodeFile << "equ i" << endl;
        }
}
//Writes code corresponding to the "not equal to" comparison of the int values occuping the two topmost locations on the stack.
void Writer::neqTop2(){
        if(!errFlag){
	    gpCodeFile << "neq i" << endl;
        }
}
	
//I/O OPERATIONS
//Writes code corresponding to the write I/O operation of the value on top of the stack.
void Writer::printTop(){
        if(!errFlag){
	    gpCodeFile << "prin" << endl;
        }
}
//Writes code corresponding to the read I/O operation whose result is put on top of the stack.
void Writer::readToTop(){
        if(!errFlag){
	    gpCodeFile << "read" << endl;
        }
}
	
//JUMP OPERATIONS
//Writes code corresponding to the definition of a label to allow jumps.
void Writer::defLabel(string label){
        if(!errFlag){
	    gpCodeFile << "define " << label << endl;
        }
}

//Writes code corresponding to the unconditional jump to label.
void Writer::uncondJumpTo(string label){
        if(!errFlag){
	    gpCodeFile << "ujp " << label << endl;
        }
}
//Writes code corresponding to the conditional jump to label only if the value on top of the stack is false.
void Writer::condFalseJumpTo(string label){
        if(!errFlag){
	    gpCodeFile << "fjp " << label << endl;
        }
}
	
//SUBROUTINE OPERATIONS
//Writes code corresponding to the allocation of a new stack frame, given the current nesting (0 : Main function, 1 : Subroutine).
void Writer::allocSF(int currNest){
        if(!errFlag){
            gpCodeFile << "mst " << currNest << endl;
        }
}
//Writes code corresponding to the function call of funcName with parNum parameters.
void Writer::callFunc(int parNum,string funcName){
        if(!errFlag){
	    gpCodeFile << "cup " << parNum << " " << funcName << endl;
        }
}
//Writes code corresponding to the return instruction of a function.
void Writer::retFunc(){
        if(!errFlag){
	    gpCodeFile << "retf" << endl;
        }
}
//Writes code corresponding to the return instruction of a procedure.
void Writer::retProc(){
        if(!errFlag){
	    gpCodeFile << "retp" << endl;
        }
}

	
//SUBROUTINE PUSH OPERATIONS
//Writes code corresponding to the move of a int value from sourceAddr on top of the current stack frame.
void Writer::movIntTopInScope(int sourceAddr){
        if(!errFlag){
	    gpCodeFile << "lod i 0 " << sourceAddr << endl;
        }
}

//Writes code corresponding to the move of a bool value from sourceAddr on top of the current stack frame.
void Writer::movBoolTopInScope(int sourceAddr){
        if(!errFlag){
	    gpCodeFile << "lod b 0 " << sourceAddr << endl;
        }
}
//Writes code corresponding to the move of a int value from sourceAddr on top of the global stack frame.
void Writer::movIntTopOutScope(int sourceAddr){
        if(!errFlag){
            gpCodeFile << "lod i 1 " << sourceAddr << endl;
        }
}
//Writes code corresponding to the move of a bool value from sourceAddr on top of the global stack frame.
void Writer::movBoolTopOutScope(int sourceAddr){
        if(!errFlag){
	    gpCodeFile << "lod b 1 " << sourceAddr << endl;
        }
}
	
//SUBROUTINE POP OPERATIONS
//Writes code corresponding to the store of the int value on top of the stack at destAddr relative address with respect to current stack frame.
void Writer::storeIntInScope(int destAddr){
        if(!errFlag){
	    gpCodeFile << "str i 0 " << destAddr << endl;
        }
}
//Writes code corresponding to the store of the bool value on top of the stack at destAddr relative address with respect to current stack frame.
void Writer::storeBoolInScope(int destAddr){
        if(!errFlag){
            gpCodeFile << "str b 0 " << destAddr << endl;
        }
}
//Writes code corresponding to the store of the int value on top of the stack at destAddr absolute address. 
void Writer::storeIntOutScope(int destAddr){
        if(!errFlag){
	    gpCodeFile << "str i 1 " << destAddr << endl;
        }
}
//Writes code corresponding to the store of the bool value on top of the stack at destAddr absolute address.
void Writer::storeBoolOutScope(int destAddr){
        if(!errFlag){
	    gpCodeFile << "str b 1 " << destAddr << endl;
        }
}
//Writes code corresponding to the end of the program.
void Writer::endProg(){
        if(!errFlag){
	    gpCodeFile << "stp" << endl;
        }
}

void Writer::errorReport(ErrorsP errType,string argument){
    
    string errorDescr;
    bool oldErrFlag = errFlag;
    
    errFlag = true;
    
    if(oldErrFlag == false && errFlag == true){
        gpCodeFile << "############ Code production ended due to error #############" << endl;
        gpCodeFile.flush();
        gpCodeFile.close();
    }
    
    cerr << "Line " << (lineNum) << ": ";
    errorLogFile << "Line " << (lineNum) << ": ";
    
    switch(errType){
        case DUPPROGID: errorDescr += "Any identifier in the program must differ from the program identifier";break;
        case GRTMIS: errorDescr += "Type mismatch with respect to > operator";break;
        case LESMIS: errorDescr += "Type mismatch with respect to < operator";break; 
        case GEQMIS: errorDescr += "Type mismatch with respect to >= operator";break; 
        case LEQMIS: errorDescr += "Type mismatch with respect to <= operator";break;
        case EQUMIS: errorDescr += "Type mismatch with respect to = operator";break;
        case NEQMIS: errorDescr += "Type mismatch with respect to <> operator";break;
        case ADDMIS: errorDescr += "Type mismatch with respect to + operator";break;
        case SUBMIS: errorDescr += "Type mismatch with respect to - (binary) operator";break;
        case MULMIS: errorDescr += "Type mismatch with respect to * operator";break;
        case DIVMIS: errorDescr += "Type mismatch with respect to / operator";break;
        case ANDMIS: errorDescr += "Type mismatch with respect to and operator";break;
        case ORMIS: errorDescr += "Type mismatch with respect to or operator";break;
        case NOTMIS: errorDescr += "Type mismatch with respect to not operator";break;
        case NEGMIS: errorDescr += "Type mismatch with respect to - (unary) operator";break;
        case IFMIS: errorDescr += "If condition: Expected boolean value";break;
        case WHILEMIS: errorDescr += "While condition: Expected boolean value";break; 
        case ASSIGNMIS: errorDescr += "Left-hand side and right-hand side types does not match";break;
        case WRITEMIS: errorDescr += "Write argument " + argument + "  Expected integer value";break;
        case READMIS: errorDescr += "Read argument " + argument + " : Expected integer value";break;
        case DUPID: errorDescr += "Duplicate identifier -> " + argument;break;
        case WRGPARNUM: errorDescr += "Wrong parameter number in " + argument + " routine call";break;
        case PARTYPEMIS: errorDescr += argument + "-> Type mismatch while analyzing parameter list";break;
        case PROCINEXP:  errorDescr += "Type mismatch due to a call of " + argument + " procedure in an expression";break;
        case DUPPROCID: errorDescr += "Duplicate procedure identifier -> " + argument;break;
        case DUPFUNCID: errorDescr += "Duplicate procedure identifier -> " + argument;break;
        case HIDID: errorDescr += "Variable " + argument + " hides global variable with same identifier";break;
        case UNDEFID: errorDescr += "Undefined identifier -> " + argument;break;
        case PROCLEFT: errorDescr += "Procedure " + argument + " appears on the left hand side of an assignment";break;
        case SCOPEMIS: errorDescr += "Variable " + argument + " used outside the scope where was declared";break;
        case UNDEFTOK: errorDescr += "Token " + argument + "undefined for the language";break;
        case MSG: errorDescr += argument; break;
        default: errorDescr += "";break;
    }
    
    cerr << errorDescr << endl;
    errorLogFile << errorDescr << endl;
        
}


Writer::~Writer() {
    if(gpCodeFile.is_open()){
        gpCodeFile.flush();
        gpCodeFile.close();
    }
    if(errorLogFile.is_open()){
        errorLogFile.flush();
        errorLogFile.close();
    }
}
